import type { ComponentPropsWithoutRef } from 'react';
import { useEffect, useRef } from 'react';

import clsx from 'clsx';
import { animate, useInView, useMotionValue, useSpring } from 'motion/react';

import type { AnimationPlaybackControls } from 'motion/react';

/**
 * NumberTicker 组件的属性接口
 *
 * @interface NumberTickerProps
 * @extends ComponentPropsWithoutRef<'span'>
 *
 * @property {number} [decimalPlaces=0] - 小数位数，默认为 0
 * @property {number} [delay=0] - 延时时间（单位：秒），默认为 0
 * @property {'down' | 'up'} [direction='up'] - 动画方向，默认为 'up'
 * @property {number} [duration] - 动画时长（单位：秒），如果不指定则使用 spring 动画
 * @property {string} [prefix=''] - 前缀，默认为空字符串
 * @property {number} [startValue=0] - 起始值，默认为 0
 * @property {string} [suffix=''] - 后缀，默认为空字符串
 * @property {number} value - 目标值
 */
interface NumberTickerProps extends ComponentPropsWithoutRef<'span'> {
	decimalPlaces?: number;
	delay?: number;
	direction?: 'down' | 'up';
	duration?: number;
	prefix?: string;
	startValue?: number;
	suffix?: string;
	value: number;
}

/**
 * NumberTicker 组件
 *
 * @param {NumberTickerProps} props - 组件属性
 * @returns {JSX.Element} - 渲染的 JSX 元素
 *
 * @example
 * // 示例：向上滚动到目标值
 * <NumberTicker value={1000} prefix="¥" suffix="元" />
 *
 * // 示例：向下滚动到目标值
 * <NumberTicker value={500} direction="down" />
 */
const NumberTicker = ({
	className,
	decimalPlaces = 0,
	delay = 0,
	direction = 'up',
	duration,
	prefix = '',
	startValue = 0,
	suffix = '',
	value,
	...props
}: NumberTickerProps) => {
	// 创建对 span 元素的引用
	const ref = useRef<HTMLSpanElement>(null);

	// 根据方向设置结束值
	const endValue = direction === 'down' ? 0 : value;

	// 使用 motion/react 提供的 useMotionValue 创建动画值
	const motionValue = useMotionValue(direction === 'down' ? value : 0);

	// 使用 useInView 检测元素是否进入视口
	const isInView = useInView(ref, { margin: '0px', once: true });

	// 使用 useSpring 创建弹簧动画效果
	const springValue = useSpring(motionValue, {
		damping: 60,
		stiffness: 100
	});

	/**
	 * 更新文本内容
	 *
	 * @param {number} latest - 最新的动画值
	 */
	function updateTextContent(latest: number) {
		if (!ref.current) return;

		// 格式化数字
		const formattedNumber = Intl.NumberFormat('en-US', {
			maximumFractionDigits: decimalPlaces,
			minimumFractionDigits: decimalPlaces
		}).format(Number(latest.toFixed(decimalPlaces)));

		// 更新 DOM 文本内容
		ref.current.textContent = `${prefix} ${formattedNumber} ${suffix}`;
	}

	// 监听 isInView 状态，当元素进入视口时启动动画
	useEffect(() => {
		if (isInView) {
			setTimeout(() => {
				motionValue.set(endValue);
			}, delay * 1000);
		}
	}, [motionValue, isInView, delay, endValue]);

	// 根据 duration 是否存在选择不同的动画方式
	useEffect(() => {
		let animation: AnimationPlaybackControls | undefined;

		if (duration) {
			// 使用 animate 方法创建基于时间的动画
			animation = animate(
				motionValue.get(), // 起始值
				endValue, // 目标值
				{
					duration, // 动画时长（秒）
					onUpdate: latest => {
						motionValue.set(latest); // 更新 motionValue
						updateTextContent(latest); // 更新文本内容
					}
				}
			);
		} else {
			// 使用弹簧动画
			springValue.on('change', latest => {
				updateTextContent(latest);
			});
		}

		// 清理函数，取消动画
		return () => animation?.cancel();
	}, [motionValue, springValue, endValue, duration]);

	// 渲染组件
	return (
		<span
			className={clsx('inline-block tabular-nums tracking-wider ', className)}
			ref={ref}
			{...props}
		>
			{prefix} {startValue} {suffix}
		</span>
	);
};

export default NumberTicker;
